// Copyright 2015-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Ethereum ABI encoding decoding library.

#![allow(clippy::module_inception)]

mod constructor;
mod contract;
mod decoder;
mod encoder;
mod errors;
mod event;
mod event_param;
mod filter;
mod function;
mod log;
mod operation;
mod param;
pub mod param_type;
mod signature;
mod state_mutability;
pub mod token;
mod tuple_param;
mod util;

#[cfg(test)]
mod tests;

pub use ethereum_types;

pub use crate::{
	constructor::Constructor,
	contract::{Contract, Events, Functions},
	decoder::decode,
	encoder::encode,
	errors::{Error, Result},
	event::Event,
	event_param::EventParam,
	filter::{RawTopicFilter, Topic, TopicFilter},
	function::Function,
	log::{Log, LogFilter, LogParam, ParseLog, RawLog},
	param::Param,
	param_type::{ParamType, Reader},
	state_mutability::StateMutability,
	token::{Token, StrictTokenizer, LenientTokenizer, Tokenizer},
	tuple_param::TupleParam,
};
use cyfs_base::ObjectId;
use sha3::{Digest, Keccak256};
use itertools::Itertools;
use std::collections::HashMap;
use crate::ethereum_types::H256;

/// ABI word.
pub type Word = [u8; 32];

/// ABI address.
pub type Address = ObjectId;

/// ABI fixed bytes.
pub type FixedBytes = Vec<u8>;

/// ABI bytes.
pub type Bytes = Vec<u8>;

/// ABI signed integer.
pub type Int = ethereum_types::U256;

/// ABI unsigned integer.
pub type Uint = ethereum_types::U256;

/// Commonly used FixedBytes of size 32
pub type Hash = ethereum_types::H256;

/// Contract functions generated by ethabi-derive
pub trait FunctionOutputDecoder {
	/// Output types of the contract function
	type Output;

	/// Decodes the given bytes output for the contract function
	fn decode(&self, _: &[u8]) -> Result<Self::Output>;
}

pub fn load_function(abi: &str, name_or_signature: &str) -> Result<Function> {
	let contract = Contract::load_from_str(abi)?;
	let params_start = name_or_signature.find('(');

	match params_start {
		// It's a signature
		Some(params_start) => {
			let name = &name_or_signature[..params_start];

			contract
				.functions_by_name(name)?
				.iter()
				.find(|f| f.signature() == name_or_signature)
				.cloned()
				.ok_or_else(|| Error::InvalidName(name_or_signature.to_owned()))
		}

		// It's a name
		None => {
			let functions = contract.functions_by_name(name_or_signature)?;
			match functions.len() {
				0 => unreachable!(),
				1 => Ok(functions[0].clone()),
				_ => Err(Error::InvalidName(format!(
					"More than one function found for name `{}`, try providing the full signature",
					name_or_signature
				))),
			}
		}
	}
}

fn parse_tokens(params: &[(ParamType, &str)], lenient: bool) -> Result<Vec<Token>> {
	params
		.iter()
		.map(|&(ref param, value)| match lenient {
			true => LenientTokenizer::tokenize(param, value),
			false => StrictTokenizer::tokenize(param, value),
		})
		.collect::<Result<_>>()
		.map_err(From::from)
}

pub fn encode_input(abi: &str, name_or_signature: &str, values: &[String], lenient: bool) -> Result<Vec<u8>> {
	let function = load_function(abi, name_or_signature)?;
	encode_input_from_function(&function, values, lenient)
}

pub fn encode_input_from_function(function: &Function, values: &[String], lenient: bool) -> Result<Vec<u8>> {
	let params: Vec<_> =
		function.inputs.iter().map(|param| param.kind.clone()).zip(values.iter().map(|v| v as &str)).collect();
	let tokens = parse_tokens(&params, lenient)?;
	let result = function.encode_input(&tokens)?;

	Ok(result)
}

pub fn decode_input_from_function(function: &Function, input: &[u8]) -> Result<HashMap<String, String>> {
	let mut ret = HashMap::new();
	let output = function.decode_input(input)?;

	for (token, param) in output.iter().zip(function.inputs.iter()) {
		println!("get token type {:?}", token);
		ret.insert(param.name.clone(), token.to_string());
	}

	return Ok(ret);
}

pub fn encode_constructor(abi: &str, code: Bytes, values: &[String], lenient: bool) -> Result<Bytes> {
	let contract = Contract::load_from_str(abi)?;
	let constructor = contract.constructor().ok_or(Error::InvalidName("contract has no constractor".to_owned()))?;
	let result = constructor.encode_constructor(code, values, lenient)?;
	Ok(result)
}

pub fn decode_call_output(abi: &str, name_or_signature: &str, data: Vec<u8>) -> Result<String> {
	let function = load_function(abi, name_or_signature)?;
	decode_call_output_from_function(&function, data)
}

pub fn decode_call_output_from_function(function: &Function, data: Vec<u8>) -> Result<String> {
	let tokens = function.decode_output(&data)?;

	assert_eq!(function.outputs.len(), tokens.len());

	let result = function.outputs
		.iter()
		.zip(tokens.iter())
		.map(|(ty, to)| format!("{} {}", ty.kind, to))
		.collect::<Vec<String>>()
		.join("\n");

	Ok(result)
}

fn hash_signature(sig: &str) -> Hash {
	Hash::from_slice(Keccak256::digest(&sig.replace(" ", "").as_bytes()).as_slice())
}

pub fn encode_topics(abi: &str, name_or_signature: &str, params: Vec<Option<String>>) -> Result<Vec<Option<H256>>> {
	let event = load_event(abi, name_or_signature)?;
	encode_topics_from_event(&event, params)
}

pub fn encode_topics_from_event(event: &Event, params: Vec<Option<String>>) -> Result<Vec<Option<H256>>> {
	let mut topics = vec![];
	let topic0 = event.signature();
	topics.push(Some(topic0));
	if params.len() > event.inputs.len() {
		return Err(Error::InvalidData);
	}
	for i in 0..params.len() {
		if params[i].is_none() {
			topics.push(None);
		} else {
			let token = LenientTokenizer::tokenize(&event.inputs[i].kind, params[i].as_ref().unwrap().as_str())?;
			let topic = encode(&vec![token]);
			topics.push(Some(Hash::from_slice(&topic)));
		}
	}

	return Ok(topics);
}

pub fn load_event(abi: &str, name_or_signature: &str) -> Result<Event> {
	let contract = Contract::load_from_str(abi)?;
	let params_start = name_or_signature.find('(');

	match params_start {
		// It's a signature.
		Some(params_start) => {
			let name = &name_or_signature[..params_start];
			let signature = hash_signature(name_or_signature);
			contract
				.events_by_name(name)?
				.iter()
				.find(|event| event.signature() == signature)
				.cloned()
				.ok_or_else(|| Error::InvalidName(name_or_signature.to_owned()))
		}

		// It's a name.
		None => {
			let events = contract.events_by_name(name_or_signature)?;
			match events.len() {
				0 => unreachable!(),
				1 => Ok(events[0].clone()),
				_ => Err(Error::InvalidName(format!(
					"More than one function found for name `{}`, try providing the full signature",
					name_or_signature
				))),
			}
		}
	}
}

pub fn decode_from_log(abi: &str, log: &evm::Log) -> Result<(String, String)> {
	let contract = Contract::load_from_str(abi)?;
	let events_hash: HashMap<Hash, Event> = contract.events().map(|e|(e.signature(), e.clone())).collect();

	if let Some(event) = events_hash.get(&log.topics[0]) {
		let decoded = event.parse_log((log.topics.clone(), log.data.clone()).into())?;

		let result = decoded
			.params
			.into_iter()
			.map(|log_param| format!("{} {}", log_param.name, log_param.value))
			.collect::<Vec<String>>()
			.join("\n");

		Ok((event.name.clone(), result))
	} else {
		// 先不支持匿名Event
		Err(Error::InvalidData)
	}

}

pub fn decode_log(abi: &str, name_or_signature: &str, topics: Vec<Hash>, data: Vec<u8>) -> Result<String> {
	let event = load_event(abi, name_or_signature)?;
	let decoded = event.parse_log((topics, data).into())?;

	let result = decoded
		.params
		.into_iter()
		.map(|log_param| format!("{} {}", log_param.name, log_param.value))
		.collect::<Vec<String>>()
		.join("\n");

	Ok(result)
}

pub fn encode_params(params: &[String], lenient: bool) -> Result<String> {
	assert_eq!(params.len() % 2, 0);

	let params = params
		.iter()
		.tuples::<(_, _)>()
		.map(|(x, y)| Reader::read(x).map(|z| (z, y.as_str())))
		.collect::<Result<Vec<_>>>()?;

	let tokens = parse_tokens(params.as_slice(), lenient)?;
	let result = encode(&tokens);

	Ok(hex::encode(&result))
}

pub fn decode_params(types: &[String], data: &str) -> Result<String> {
	let types: Vec<ParamType> = types.iter().map(|s| Reader::read(s)).collect::<Result<_>>()?;

	let data: Vec<u8> = hex::decode(&data)?;

	let tokens = decode(&types, &data)?;

	assert_eq!(types.len(), tokens.len());

	let result =
		types.iter().zip(tokens.iter()).map(|(ty, to)| format!("{} {}", ty, to)).collect::<Vec<String>>().join("\n");

	Ok(result)
}